package dal

import (
	"fmt"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"starry_sky/internal/core"
)

/**************************************基础操作**************************************/
// Insert 创建对象
func baseInsert[T any](DB *gorm.DB, t *T) error {
	return DB.Create(t).Error
}

// UpdateForMap 根据map更新
func baseUpdateForMap[T any](DB *gorm.DB, t *T, m map[string]any) error {
	return DB.Model(t).Updates(m).Error
}

// Update 全属性更新
func baseSave[T any](DB *gorm.DB, t *T) error {
	return DB.Save(t).Error
}

// UpdateForObj 忽略默认属性更新
func baseUpdateForObj[T any](DB *gorm.DB, t *T) error {
	return DB.Updates(t).Error
}

// Delete 逻辑删除
func baseDelete[T any](DB *gorm.DB, t *T) error {
	return DB.Delete(t).Error
}

func baseDeleteForMap[T any](DB *gorm.DB, t *T, m map[string]any) error {
	return DB.Where(m).Delete(t).Error
}

// RealDelete 物理删除
func baseRealDelete[T any](DB *gorm.DB, t *T) error {
	return DB.Unscoped().Delete(t).Error
}

func baseRealDeleteForMap[T any](DB *gorm.DB, t *T, m map[string]any) error {
	return DB.Unscoped().Where(m).Delete(t).Error
}

// UniqueInstall 唯一性插入
func baseUniqueInstall[T any](DB *gorm.DB, t *T, uniqueColumn string) error {
	return DB.Clauses(clause.OnConflict{
		Columns:   []clause.Column{{Name: uniqueColumn}},
		DoNothing: true,
	}).Create(t).Error
}

/**************************************查询类基础操作**************************************/

// FindById 跟具id查询，返回指定字段,可进行预加载
func FindByIdSelect[T any](t *T, str string, names ...string) error {
	tx := DB.Model(t).Select(str)
	for _, name := range names {
		tx = DB.Preload(name)
	}
	return tx.First(t).Error
}

// FindById 更具id查询,可进行预加载
func FindById[T any](t *T, names ...string) error {
	tx := DB.Model(t)
	for _, name := range names {
		tx = DB.Preload(name)
	}
	return tx.First(t).Error
}

// GetAll 得到全部记录
func GetAll[T any](list *[]T) error {
	return DB.Order("created_at desc").Find(list).Error
}

func GetAllPreload[T any](list *[]T, names ...string) error {
	tx := DB.Model(new(T))
	for _, name := range names {
		tx = DB.Preload(name)
	}
	return tx.First(list).Error
}

// FindListForMap 根据map条件查询对象数组
func FindListForMap[T any](list *[]T, queryMap map[string]any) error {
	return DB.Where(queryMap).Order("id").Find(list).Error
}

// FindListForMapOrderBy 根据map条件查询对象数组,并且根据字段排序
func FindListForMapOrderBy[T any](list *[]T, queryMap map[string]any, str string) error {
	return DB.Where(queryMap).Order(str).Find(list).Error
}

// FindListForObj 根据对象数据查询对象数组
func FindListForObj[T any](list *[]T, t *T) error {
	return DB.Where(t).Order("id").Find(list).Error
}

// FindPage 基础分页
func FindPage[T any](pageSupport *core.PageSupport[T], wrapper map[string]any, preloadNames []string, keyColumn ...string) error {
	var queryModel T
	tx := DB.Model(&queryModel).
		Where(wrapper)
	for _, s := range keyColumn {
		tx = tx.Where(s+" like ?", "%"+pageSupport.Keyword+"%")
	}
	if len(preloadNames) > 0 {
		for _, preloadName := range preloadNames {
			tx = tx.Preload(preloadName)
		}
	}
	err := tx.Count(&pageSupport.TotalCount).
		Order("created_at desc").
		Offset(pageSupport.GetOffSet()).
		Limit(pageSupport.GetPageSize()).
		Find(&pageSupport.Data).Error
	return err
}

// CountByMap 得到数量
// t 需要查询的实体
// count 保存数量的地址
// query 查询条件
func CountByMap[T any](count *int64, queryMap map[string]any) error {
	var t *T
	return DB.Model(t).Where(queryMap).Count(count).Error
}

// JudgmentRepetition 判断实体为一项是否唯一
func JudgmentRepetition[T any](count *int64, queryMap map[string]any, id string) error {
	var t *T
	if id == core.EmptyID {
		return DB.Model(t).Where(queryMap).Count(count).Error
	} else {
		return DB.Model(t).Where(queryMap).Where("id != ?", id).Count(count).Error
	}
}

/**************************************无事务操作**************************************/

// Insert 创建对象
func Insert[T any](t *T) error {
	return baseInsert(DB, t)
}

// UpdateForMap 根据map更新
func UpdateForMap[T any](t *T, m map[string]any) error {
	return baseUpdateForMap(DB, t, m)
}

// Update 全属性更新
func Save[T any](t *T) error {
	return baseSave(DB, t)
}

// UpdateForObj 忽略默认属性更新
func UpdateForObj[T any](t *T) error {
	return baseUpdateForObj(DB, t)
}

// Delete 逻辑删除
func Delete[T any](t *T) error {
	return baseDelete(DB, t)
}

func DeleteForMap[T any](t *T, m map[string]any) error {
	return baseDeleteForMap(DB, t, m)
}

// RealDelete 物理删除
func RealDelete[T any](t *T) error {
	return baseRealDelete(DB, t)
}

func RealDeleteForMap[T any](t *T, m map[string]any) error {
	return baseRealDeleteForMap(DB, t, m)
}

// UniqueInstall 唯一性插入
func UniqueInstall[T any](t *T, uniqueColumn string) error {
	return baseUniqueInstall(DB, t, uniqueColumn)
}

/**************************************事务相关操作**************************************/

// TransactionBegin 开启一个事务
func TransactionBegin() *gorm.DB {
	return DB.Begin()
}

// TransactionCommit 事务提交
func TransactionCommit(tx *gorm.DB) error {
	return tx.Commit().Error
}

// TransactionRollback 事务回滚
func TransactionRollback(tx *gorm.DB) error {
	return tx.Rollback().Error
}

// TransactionInsert 创建对象
func TransactionInsert[T any](tx *gorm.DB, t *T) error {
	return baseInsert(tx, t)
}

// TransactionUpdateForMap 根据map更新
func TransactionUpdateForMap[T any](tx *gorm.DB, t *T, m map[string]any) error {
	return baseUpdateForMap(tx, t, m)
}

// TransactionUpdate 全属性更新
func TransactionSave[T any](tx *gorm.DB, t *T) error {
	return baseSave(tx, t)
}

// TransactionUpdateForObj 忽略默认属性更新
func TransactionUpdateForObj[T any](tx *gorm.DB, t *T) error {
	return baseUpdateForObj(tx, t)
}

// TransactionDelete 逻辑删除
func TransactionDelete[T any](tx *gorm.DB, t *T) error {
	return baseDelete(tx, t)
}

func TransactionDeleteForMap[T any](tx *gorm.DB, t *T, m map[string]any) error {
	return baseDeleteForMap(tx, t, m)
}

// TransactionRealDelete 物理删除
func TransactionRealDelete[T any](tx *gorm.DB, t *T) error {
	return baseRealDelete(tx, t)
}

func TransactionRealDeleteForMap[T any](tx *gorm.DB, t *T, m map[string]any) error {
	return baseRealDeleteForMap(tx, t, m)
}

// TransactionUniqueInstall 唯一性插入
func TransactionUniqueInstall[T any](tx *gorm.DB, t *T, uniqueColumn string) error {
	return baseUniqueInstall(tx, t, uniqueColumn)
}

/**************************************原生sql操作**************************************/

// GetRowBySql 执行原生sql,单行放回
func GetRowBySql(sql string) (map[string]any, error) {
	var returnMap map[string]any
	err := DB.Raw(sql).Scan(&returnMap).Error
	if err != nil {
		return nil, err
	}
	return returnMap, nil
}

// ExecuteSql 执行原生sql
func ExecuteSql(sql string) error {
	return DB.Exec(sql).Error
}

// GetListRowBySql 执行原生sql,多行放回
func GetListRowBySql(sql string) ([]map[string]any, error) {
	var returnRows []map[string]any
	err := DB.Raw(sql).Scan(&returnRows).Error
	if err != nil {
		return nil, err
	}
	return returnRows, nil
}

// PaginationBySql 通过原生sql语句进行
func PaginationBySql(sql string, pageSize uint, offset uint) (int64, []map[string]any, error) {
	countSql := fmt.Sprintf("select count(*) as num from (%s) countNum", sql)
	countMap, err := GetRowBySql(countSql)
	if err != nil {
		return 0, nil, err
	}
	totalCount := countMap["num"].(int64)
	querySql := fmt.Sprintf("%s limit %d offset %d", sql, pageSize, offset)
	queryMaps, err := GetListRowBySql(querySql)
	if err != nil {
		return 0, nil, err
	}
	return totalCount, queryMaps, nil
}

/**************************************sql模板操作操作**************************************/

// GetRowByTmpl 执行模板sql,单行放回
func GetRowByTmpl(tmplName string, data any) (map[string]any, error) {
	sql, err := SqlTemplate.GetSql(tmplName, data)
	if err != nil {
		return nil, err
	}
	return GetRowBySql(sql)
}

// GetListRowByTmpl 执行模板sql,多行放回
func GetListRowByTmpl(tmplName string, data any) ([]map[string]any, error) {
	sql, err := SqlTemplate.GetSql(tmplName, data)
	if err != nil {
		return nil, err
	}
	return GetListRowBySql(sql)
}

// ExecuteSqlByTmpl 执行模板sql
func ExecuteSqlByTmpl(tmplName string, data any) error {
	sql, err := SqlTemplate.GetSql(tmplName, data)
	if err != nil {
		return err
	}
	err = ExecuteSql(sql)
	if err != nil {
		return err
	}
	return nil
}

// PaginationByTmpl 执行sql模板进行分页查询
func PaginationByTmpl(tmplName string, data any, pageSize uint, offset uint) (int64, []map[string]any, error) {
	sql, err := SqlTemplate.GetSql(tmplName, data)
	if err != nil {
		return 0, nil, err
	}
	return PaginationBySql(sql, pageSize, offset)
}
